// 13.1 拷贝、赋值与销毁
/**
 * 我们将以最基本的操作——拷贝构造函数、拷贝赋值运算符和析构函数作为开始。我们在13.6节（第470页）中将介绍移动操作（新标准所引入的操作）。
 */

#include <iterator>
#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array, std::stack, std::queue;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::begin, std::cbegin, std::end, std::cend, std::find, std::accumulate, std::equal, std::fill, std::fill_n, std::back_inserter;
using std::cin, std::cout, std::endl;
using std::copy, std::replace, std::replace_copy;
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <memory>
#include <new>
using namespace std;

// 演示使用=delete完成阻止拷贝和阻止赋值
struct NoCopy
{
    NoCopy() = default;                         // 合成的默认构造函数
    NoCopy(const NoCopy &) = delete;            // 阻止拷贝
    NoCopy &operator=(const NoCopy &) = delete; // 阻止赋值
    ~NoCopy() = default;                        // 合成的析构函数
};

// 演示private拷贝控制
class PrivateCopy
{
    // 无访问说明符：接下来的成员默认为private的
    // 拷贝控制成员是private的，因此普通用户代码无法访问（友元可以访问）
    PrivateCopy(const PrivateCopy&); // 拷贝构造函数
    PrivateCopy &operator=(const PrivateCopy&);
public:
    PrivateCopy() = default; // 合成的默认构造函数
    ~PrivateCopy(); // 用户可以定义此类型的对象，但无法拷贝它们
};

int main()
{
    // 13.1.6 阻止拷贝
    // 大多数类应该定义默认构造函数、拷贝构造函数和拷贝赋值运算符，无论是隐式地还是显式地。但对某些类来说，这些操作没有合理的意义。
    // 虽然大多数类应该定义（而且通常也的确定义了）拷贝构造函数和拷贝赋值运算符，但对某些类来说，这些操作没有合理的意义。在此情况下，定义类时必须采用某种机制阻止拷贝或赋值。
    // 为了阻止拷贝，看起来可能应该不定义拷贝控制成员。但是，这种策略是无效的：如果我们的类未定义这些操作，编译器为它生成合成的版本。

    // 定义删除的函数
    // 在新标准下，我们可以通过将拷贝构造函数和拷贝赋值运算符定义为删除的函数（deleted function）来阻止拷贝。
    // 删除的函数是这样一种函数：我们虽然声明了它们，但不能以任何方式使用它们。
    // 在函数的参数列表后面加上=delete来指出我们希望将它定义为删除的：[插图]
    {
        NoCopy np;
    }
    // 与=default不同，=delete必须出现在函数第一次声明的时候
    // 与=default的另一个不同之处是，我们可以对任何函数指定=delete（我们只能对编译器可以合成的默认构造函数或拷贝控制成员使用=default）。
    // 删除函数的主要用途是禁止拷贝控制成员，但当我们希望引导函数匹配过程时，删除函数有时也是有用的。

    // 析构函数不能是删除的函数
    // 如果析构函数被删除，就无法销毁此类型的对象了。对于一个删除了析构函数的类型，编译器将不允许定义该类型的变量或创建该类的临时对象。
    // 而且，如果一个类有某个成员的类型删除了析构函数，我们也不能定义该类的变量或临时对象。

    // 合成的拷贝控制成员可能是删除的
    // 对某些类来说，编译器将这些合成的成员定义为删除的函数：
    // 1.· 如果类的某个成员的析构函数是删除的或不可访问的（例如，是private的），则类的合成析构函数被定义为删除的。
    // 2.· 如果类的某个成员的拷贝构造函数是删除的或不可访问的，则类的合成拷贝构造函数被定义为删除的。如果类的某个成员的析构函数是删除的或不可访问的，则类合成的拷贝构造函数也被定义为删除的。
    // 3.· 如果类的某个成员的拷贝赋值运算符是删除的或不可访问的，或是类有一个const的或引用成员，则类的合成拷贝赋值运算符被定义为删除的。
    // 4.· 如果类的某个成员的析构函数是删除的或不可访问的，或是类有一个引用成员，它没有类内初始化器（参见2.6.1节，第65页），或是类有一个const成员，它没有类内初始化器且其类型未显式定义默认构造函数，
    //     则该类的默认构造函数被定义为删除的。
    // 本质上，这些规则的含义是：如果一个类有数据成员不能默认构造、拷贝、复制或销毁，则对应的成员函数将被定义为删除的。
    // 一个成员有删除的或不可访问的析构函数会导致合成的默认和拷贝构造函数被定义为删除的，这看起来可能有些奇怪。
    // 其原因是，如果没有这条规则，我们可能会创建出无法销毁的对象。
    // 如果一个类有const成员，则它不能使用合成的拷贝赋值运算符。毕竟，此运算符试图赋值所有成员，而将一个新值赋予一个const对象是不可能的。
    // 对于有引用成员的类，合成拷贝赋值运算符被定义为删除的。
    // 本质上，当不可能拷贝、赋值或销毁类的成员时，类的合成拷贝控制成员就被定义为删除的。

    // private拷贝控制
    // 在新标准发布之前，类是通过将其拷贝构造函数和拷贝赋值运算符声明为private的来阻止拷贝：[插图]
    {
        PrivateCopy pc;
    }
    // 希望阻止拷贝的类应该使用=delete来定义它们自己的拷贝构造函数和拷贝赋值运算符，而不应该将它们声明为private的。


    return 0;
}